home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / g_combat.c1 < prev    next >
Encoding:
Text File  |  2002-12-07  |  15.1 KB  |  586 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. // g_combat.c
  21.  
  22. #include "g_local.h"
  23.  
  24. /*
  25. ============
  26. CanDamage
  27.  
  28. Returns true if the inflictor can directly damage the target.  Used for
  29. explosions and melee attacks.
  30. ============
  31. */
  32. qboolean CanDamage (edict_t *targ, edict_t *inflictor)
  33. {
  34.     vec3_t    dest;
  35.     trace_t    trace;
  36.  
  37. // bmodels need special checking because their origin is 0,0,0
  38.     if (targ->movetype == MOVETYPE_PUSH)
  39.     {
  40.         VectorAdd (targ->absmin, targ->absmax, dest);
  41.         VectorScale (dest, 0.5, dest);
  42.         trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
  43.         if (trace.fraction == 1.0)
  44.             return true;
  45.         if (trace.ent == targ)
  46.             return true;
  47.         return false;
  48.     }
  49.     
  50.     trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID);
  51.     if (trace.fraction == 1.0)
  52.         return true;
  53.  
  54.     VectorCopy (targ->s.origin, dest);
  55.     dest[0] += 15.0;
  56.     dest[1] += 15.0;
  57.     trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
  58.     if (trace.fraction == 1.0)
  59.         return true;
  60.  
  61.     VectorCopy (targ->s.origin, dest);
  62.     dest[0] += 15.0;
  63.     dest[1] -= 15.0;
  64.     trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
  65.     if (trace.fraction == 1.0)
  66.         return true;
  67.  
  68.     VectorCopy (targ->s.origin, dest);
  69.     dest[0] -= 15.0;
  70.     dest[1] += 15.0;
  71.     trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
  72.     if (trace.fraction == 1.0)
  73.         return true;
  74.  
  75.     VectorCopy (targ->s.origin, dest);
  76.     dest[0] -= 15.0;
  77.     dest[1] -= 15.0;
  78.     trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
  79.     if (trace.fraction == 1.0)
  80.         return true;
  81.  
  82.  
  83.     return false;
  84. }
  85.  
  86.  
  87. /*
  88. ============
  89. Killed
  90. ============
  91. */
  92. void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
  93. {
  94.     if (targ->health < -999)
  95.         targ->health = -999;
  96.  
  97.     targ->enemy = attacker;
  98.  
  99.     if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
  100.     {
  101. //        targ->svflags |= SVF_DEADMONSTER;    // now treat as a different content type
  102.         if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY))
  103.         {
  104.             level.killed_monsters++;
  105.             if (coop->value && attacker->client)
  106.                 attacker->client->resp.score++;
  107.             // medics won't heal monsters that they kill themselves
  108.             if (strcmp(attacker->classname, "monster_medic") == 0)
  109.                 targ->owner = attacker;
  110.         }
  111.     }
  112.  
  113.     if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE)
  114.     {    // doors, triggers, etc
  115.         targ->die (targ, inflictor, attacker, damage, point);
  116.         return;
  117.     }
  118.  
  119.     if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
  120.     {
  121.         targ->touch = NULL;
  122.         monster_death_use (targ);
  123.     }
  124.  
  125.     targ->die (targ, inflictor, attacker, damage, point);
  126. }
  127.  
  128.  
  129. /*
  130. ================
  131. SpawnDamage
  132. ================
  133. */
  134. void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage)
  135. {
  136.     if (damage > 255)
  137.         damage = 255;
  138.     gi.WriteByte (svc_temp_entity);
  139.     gi.WriteByte (type);
  140. //    gi.WriteByte (damage);
  141.     gi.WritePosition (origin);
  142.     gi.WriteDir (normal);
  143.     gi.multicast (origin, MULTICAST_PVS);
  144. }
  145.  
  146.  
  147. /*
  148. ============
  149. T_Damage
  150.  
  151. targ        entity that is being damaged
  152. inflictor    entity that is causing the damage
  153. attacker    entity that caused the inflictor to damage targ
  154.     example: targ=monster, inflictor=rocket, attacker=player
  155.  
  156. dir            direction of the attack
  157. point        point at which the damage is being inflicted
  158. normal        normal vector from that point
  159. damage        amount of damage being inflicted
  160. knockback    force to be applied against targ as a result of the damage
  161.  
  162. dflags        these flags are used to control how T_Damage works
  163.     DAMAGE_RADIUS            damage was indirect (from a nearby explosion)
  164.     DAMAGE_NO_ARMOR            armor does not protect from this damage
  165.     DAMAGE_ENERGY            damage is from an energy based weapon
  166.     DAMAGE_NO_KNOCKBACK        do not affect velocity, just view angles
  167.     DAMAGE_BULLET            damage is from a bullet (used for ricochets)
  168.     DAMAGE_NO_PROTECTION    kills godmode, armor, everything
  169. ============
  170. */
  171. static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags)
  172. {
  173.     gclient_t    *client;
  174.     int            save;
  175.     int            power_armor_type;
  176.     int            index;
  177.     int            damagePerCell;
  178.     int            pa_te_type;
  179.     int            power;
  180.     int            power_used;
  181.  
  182.     if (!damage)
  183.         return 0;
  184.  
  185.     client = ent->client;
  186.  
  187.     if (dflags & DAMAGE_NO_ARMOR)
  188.         return 0;
  189.  
  190.     if (client)
  191.     {
  192.         power_armor_type = PowerArmorType (ent);
  193.         if (power_armor_type != POWER_ARMOR_NONE)
  194.         {
  195.             index = ITEM_INDEX(FindItem("Cells"));
  196.             power = client->pers.inventory[index];
  197.         }
  198.     }
  199.     else if (ent->svflags & SVF_MONSTER)
  200.     {
  201.         power_armor_type = ent->monsterinfo.power_armor_type;
  202.         power = ent->monsterinfo.power_armor_power;
  203.     }
  204.     else
  205.         return 0;
  206.  
  207.     if (power_armor_type == POWER_ARMOR_NONE)
  208.         return 0;
  209.     if (!power)
  210.         return 0;
  211.  
  212.     if (power_armor_type == POWER_ARMOR_SCREEN)
  213.     {
  214.         vec3_t        vec;
  215.         float        dot;
  216.         vec3_t        forward;
  217.  
  218.         // only works if damage point is in front
  219.         AngleVectors (ent->s.angles, forward, NULL, NULL);
  220.         VectorSubtract (point, ent->s.origin, vec);
  221.         VectorNormalize (vec);
  222.         dot = DotProduct (vec, forward);
  223.         if (dot <= 0.3)
  224.             return 0;
  225.  
  226.         damagePerCell = 1;
  227.         pa_te_type = TE_SCREEN_SPARKS;
  228.         damage = damage / 3;
  229.     }
  230.     else
  231.     {
  232.         damagePerCell = 2;
  233.         pa_te_type = TE_SHIELD_SPARKS;
  234.         damage = (2 * damage) / 3;
  235.     }
  236.  
  237.     save = power * damagePerCell;
  238.     if (!save)
  239.         return 0;
  240.     if (save > damage)
  241.         save = damage;
  242.  
  243.     SpawnDamage (pa_te_type, point, normal, save);
  244.     ent->powerarmor_time = level.time + 0.2;
  245.  
  246.     power_used = save / damagePerCell;
  247.  
  248.     if (client)
  249.         client->pers.inventory[index] -= power_used;
  250.     else
  251.         ent->monsterinfo.power_armor_power -= power_used;
  252.     return save;
  253. }
  254.  
  255. static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags)
  256. {
  257.     gclient_t    *client;
  258.     int            save;
  259.     int            index;
  260.     gitem_t        *armor;
  261.  
  262.     if (!damage)
  263.         return 0;
  264.  
  265.     client = ent->client;
  266.  
  267.     if (!client)
  268.         return 0;
  269.  
  270.     if (dflags & DAMAGE_NO_ARMOR)
  271.         return 0;
  272.  
  273.     index = ArmorIndex (ent);
  274.     if (!index)
  275.         return 0;
  276.  
  277.     armor = GetItemByIndex (index);
  278.  
  279.     if (dflags & DAMAGE_ENERGY)
  280.         save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage);
  281.     else
  282.         save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage);
  283.     if (save >= client->pers.inventory[index])
  284.         save = client->pers.inventory[index];
  285.  
  286.     if (!save)
  287.         return 0;
  288.  
  289.     client->pers.inventory[index] -= save;
  290.     SpawnDamage (te_sparks, point, normal, save);
  291.  
  292.     return save;
  293. }
  294.  
  295. void M_ReactToDamage (edict_t *targ, edict_t *attacker)
  296. {
  297.     if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
  298.         return;
  299.  
  300.     if (attacker == targ || attacker == targ->enemy)
  301.         return;
  302.  
  303.     // if we are a good guy monster and our attacker is a player
  304.     // or another good guy, do not get mad at them
  305.     if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
  306.     {
  307.         if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
  308.             return;
  309.     }
  310.  
  311.     // we now know that we are not both good guys
  312.  
  313.     // if attacker is a client, get mad at them because he's good and we're not
  314.     if (attacker->client)
  315.     {
  316.         targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
  317.  
  318.         // this can only happen in coop (both new and old enemies are clients)
  319.         // only switch if can't see the current enemy
  320.         if (targ->enemy && targ->enemy->client)
  321.         {
  322.             if (visible(targ, targ->enemy))
  323.             {
  324.                 targ->oldenemy = attacker;
  325.                 return;
  326.             }
  327.             targ->oldenemy = targ->enemy;
  328.         }
  329.         targ->enemy = attacker;
  330.         if (!(targ->monsterinfo.aiflags & AI_DUCKED))
  331.             FoundTarget (targ);
  332.         return;
  333.     }
  334.  
  335.     // it's the same base (walk/swim/fly) type and a different classname and it's not a tough guy
  336.     // (could be general, leader or bigmech), get mad at them
  337.     if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) &&
  338.          (strcmp (targ->classname, attacker->classname) != 0) &&
  339.          (attacker->max_health < 400) )
  340.     {
  341.         if (targ->enemy && targ->enemy->client)
  342.             targ->oldenemy = targ->enemy;
  343.         targ->enemy = attacker;
  344.         if (!(targ->monsterinfo.aiflags & AI_DUCKED))
  345.             FoundTarget (targ);
  346.     }
  347.     // if they *meant* to shoot us, then shoot back
  348.     else if (attacker->enemy == targ)
  349.     {
  350.         if (targ->enemy && targ->enemy->client)
  351.             targ->oldenemy = targ->enemy;
  352.         targ->enemy = attacker;
  353.         if (!(targ->monsterinfo.aiflags & AI_DUCKED))
  354.             FoundTarget (targ);
  355.     }
  356.     // otherwise get mad at whoever they are mad at (help our buddy) unless it is us!
  357.     else if (attacker->enemy && attacker->enemy != targ)
  358.     {
  359.         if (targ->enemy && targ->enemy->client)
  360.             targ->oldenemy = targ->enemy;
  361.         targ->enemy = attacker->enemy;
  362.         if (!(targ->monsterinfo.aiflags & AI_DUCKED))
  363.             FoundTarget (targ);
  364.     }
  365. }
  366.  
  367. qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker)
  368. {
  369.         //FIXME make the next line real and uncomment this block
  370.         // if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
  371.     return false;
  372. }
  373.  
  374. void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod)
  375. {
  376.     gclient_t    *client;
  377.     int            take;
  378.     int            save;
  379.     int            asave;
  380.     int            psave;
  381.     int            te_sparks;
  382.  
  383.     if (!targ->takedamage)
  384.         return;
  385.  
  386.     // friendly fire avoidance
  387.     // if enabled you can't hurt teammates (but you can hurt yourself)
  388.     // knockback still occurs
  389.     if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value))
  390.     {
  391.         if (OnSameTeam (targ, attacker))
  392.         {
  393.             if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE)
  394.                 damage = 0;
  395.             else
  396.                 mod |= MOD_FRIENDLY_FIRE;
  397.         }
  398.     }
  399.     meansOfDeath = mod;
  400.  
  401.     // easy mode takes half damage
  402.     if (skill->value == 0 && deathmatch->value == 0 && targ->client)
  403.     {
  404.         damage *= 0.5;
  405.         if (!damage)
  406.             damage = 1;
  407.     }
  408.  
  409.     client = targ->client;
  410.  
  411.     if (dflags & DAMAGE_BULLET)
  412.         te_sparks = TE_BULLET_SPARKS;
  413.     else
  414.         te_sparks = TE_SPARKS;
  415.  
  416.     VectorNormalize(dir);
  417.  
  418. // bonus damage for suprising a monster
  419.     if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
  420.         damage *= 2;
  421.  
  422.     if (targ->flags & FL_NO_KNOCKBACK)
  423.         knockback = 0;
  424.  
  425. // figure momentum add
  426.     if (!(dflags & DAMAGE_NO_KNOCKBACK))
  427.     {
  428.         if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
  429.         {
  430.             vec3_t    kvel;
  431.             float    mass;
  432.  
  433.             if (targ->mass < 50)
  434.                 mass = 50;
  435.             else
  436.                 mass = targ->mass;
  437.  
  438.             if (targ->client  && attacker == targ)
  439.                 VectorScale (dir, 1600.0 * (float)knockback / mass, kvel);    // the rocket jump hack...
  440.             else
  441.                 VectorScale (dir, 500.0 * (float)knockback / mass, kvel);
  442.  
  443.             VectorAdd (targ->velocity, kvel, targ->velocity);
  444.         }
  445.     }
  446.  
  447.     take = damage;
  448.     save = 0;
  449.  
  450.     // check for godmode
  451.     if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) )
  452.     {
  453.         take = 0;
  454.         save = damage;
  455.         SpawnDamage (te_sparks, point, normal, save);
  456.     }
  457.  
  458.     // check for invincibility
  459.     if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
  460.     {
  461.         if (targ->pain_debounce_time < level.time)
  462.         {
  463.             gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
  464.             targ->pain_debounce_time = level.time + 2;
  465.         }
  466.         take = 0;
  467.         save = damage;
  468.     }
  469.  
  470.     psave = CheckPowerArmor (targ, point, normal, take, dflags);
  471.     take -= psave;
  472.  
  473.     asave = CheckArmor (targ, point, normal, take, te_sparks, dflags);
  474.     take -= asave;
  475.  
  476.     //treat cheat/powerup savings the same as armor
  477.     asave += save;
  478.  
  479.     // team damage avoidance
  480.     if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker))
  481.         return;
  482.  
  483. // do the damage
  484.     if (take)
  485.     {
  486.         if ((targ->svflags & SVF_MONSTER) || (client))
  487.         {
  488.             if (!Q_stricmp(targ->classname, "monster_ambass") || 
  489.                 !Q_stricmp(targ->classname, "monster_saucer") ||
  490.                 !Q_stricmp(targ->classname, "npc_droid") ||
  491.                 !Q_stricmp(targ->classname, "monster_mech"))
  492.                 SpawnDamage (TE_GUNSHOT, point, normal, take);
  493.             else if (!Q_stricmp(targ->classname, "monster_martian_leader"))
  494.                 SpawnDamage (TE_LEADERFIELD, point, normal, take);
  495.             else if (client || targ->monsterinfo.aiflags & AI_NPC)
  496.                 SpawnDamage (TE_BLOOD, point, normal, take);
  497.             else
  498.                 SpawnDamage (TE_GREENBLOOD, point, normal, take);
  499.         }
  500.         else
  501.             SpawnDamage (te_sparks, point, normal, take);
  502.  
  503.  
  504.         targ->health = targ->health - take;
  505.             
  506.         if (targ->health <= 0)
  507.         {
  508.             if ((targ->svflags & SVF_MONSTER) || (client))
  509.                 targ->flags |= FL_NO_KNOCKBACK;
  510.             Killed (targ, inflictor, attacker, take, point);
  511.             return;
  512.         }
  513.     }
  514.  
  515.     if (targ->svflags & SVF_MONSTER)
  516.     {
  517.         M_ReactToDamage (targ, attacker);
  518.         if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take) && (targ->pain))
  519.         {
  520.             targ->pain (targ, attacker, knockback, take);
  521.             // nightmare mode monsters don't go into pain frames often
  522.             if (skill->value == 3)
  523.                 targ->pain_debounce_time = level.time + 5;
  524.         }
  525.     }
  526.     else if (client)
  527.     {
  528.         if (!(targ->flags & FL_GODMODE) && (take) && (targ->pain))
  529.             targ->pain (targ, attacker, knockback, take);
  530.     }
  531.     else if (take)
  532.     {
  533.         if (targ->pain)
  534.             targ->pain (targ, attacker, knockback, take);
  535.     }
  536.  
  537.     // add to the damage inflicted on a player this frame
  538.     // the total will be turned into screen blends and view angle kicks
  539.     // at the end of the frame
  540.     if (client)
  541.     {
  542.         client->damage_parmor += psave;
  543.         client->damage_armor += asave;
  544.         client->damage_blood += take;
  545.         client->damage_knockback += knockback;
  546.         VectorCopy (point, client->damage_from);
  547.     }
  548. }
  549.  
  550.  
  551. /*
  552. ============
  553. T_RadiusDamage
  554. ============
  555. */
  556. void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
  557. {
  558.     float    points;
  559.     edict_t    *ent = NULL;
  560.     vec3_t    v;
  561.     vec3_t    dir;
  562.  
  563.     while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
  564.     {
  565.         if (ent == ignore)
  566.             continue;
  567.         if (!ent->takedamage)
  568.             continue;
  569.  
  570.         VectorAdd (ent->mins, ent->maxs, v);
  571.         VectorMA (ent->s.origin, 0.5, v, v);
  572.         VectorSubtract (inflictor->s.origin, v, v);
  573.         points = damage - 0.5 * VectorLength (v);
  574.         if (ent == attacker)
  575.             points = points * 0.5;
  576.         if (points > 0)
  577.         {
  578.             if (CanDamage (ent, inflictor))
  579.             {
  580.                 VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
  581.                 T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
  582.             }
  583.         }
  584.     }
  585. }
  586.